package com.ruoyi.syrw.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.ruoyi.base.mapper.SyTypeMapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.mapper.BaseMapperPlus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.utils.redis.CacheUtils;
import com.ruoyi.report_tg.domain.vo.ExcelGeneralVo;
import com.ruoyi.syrw.component.ReportIdTL;
import com.ruoyi.syrw.component.ReportMapperEnum;
import com.ruoyi.syrw.domain.BiExcelType;
import com.ruoyi.syrw.domain.RefReportExcel;
import com.ruoyi.syrw.domain.bo.*;
import com.ruoyi.syrw.domain.vo.*;
import com.ruoyi.syrw.mapper.BiExcelTypeMapper;
import com.ruoyi.syrw.mapper.RefReportExcelMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.ycl.domain.vo.BsJctzVo;
import com.ruoyi.ycl.mapper.BsJctzMapper;
import com.ruoyi.ycl.service.IBsJctzService;
import com.ruoyi.yp.domain.BsYp;
import com.ruoyi.yp.domain.vo.BsYpVo;
import com.ruoyi.yp.mapper.BsYpMapper;
import com.sun.scenario.effect.DelegateEffect;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.ruoyi.syrw.domain.BsDataReport;
import com.ruoyi.syrw.mapper.BsDataReportMapper;
import com.ruoyi.syrw.service.IBsDataReportService;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 数据报告Service业务层处理
 *
 * @author zyb
 * @date 2024-04-30
 */
@RequiredArgsConstructor
@Service
@Transactional
@Slf4j
public class BsDataReportServiceImpl implements IBsDataReportService {


    private String url;
    private Object v;

    @Value("${qrcode.url}")
    public void setUrl(String url) {
        this.url = url;
    }

    private final BsDataReportMapper baseMapper;
    private final RefReportExcelMapper refReportExcelMapper;
    private final BiExcelTypeMapper biExcelTypeMapper;
    private final SyTypeMapper syTypeMapper;
    private final BsYpMapper ypMapper;
    private final BsJctzMapper jctzMapper;
    private final IBsJctzService jctzService;
    private final SysDeptMapper deptMapper;
    private final ISysDeptService deptService;

    /**
     * 查询数据报告
     */
    @Override
    public BsDataReportVo queryById(Long reportId) {
        BsDataReportVo bsDataReportVo = baseMapper.selectVoById(reportId);
        if (ObjUtil.isNotEmpty(bsDataReportVo)) {
            List<RefReportExcelVo> refReportExcelVos = refReportExcelMapper.selectVoList(new LambdaQueryWrapper<RefReportExcel>()
                .eq(RefReportExcel::getReportId, reportId));
//                .orderByAsc(RefReportExcel::getExcelIndex));
            refReportExcelVos.forEach(v -> {
                BiExcelType biExcelType = biExcelTypeMapper.selectById(v.getExcelTypeId());
                if (ObjUtil.isNull(biExcelType)) {
                    throw new ServiceException("根据" + v.getExcelTypeId() + "未查到相应的Excel类型，请联系管理员处理");
                }
                v.setVo(biExcelType);
                // 2024年6月6日 17:04:02 移除
//                v.setPageIndex(refReportExcelVos.stream().filter(x -> {
//                    return (x.getExcelTypeId().equals(v.getExcelTypeId()) && ObjUtil.isNotNull(x.getPageIndex()));
//                }).count() + 1);
            });
            // 2024年6月20日 15:08:20 add 排序
            List<RefReportExcelVo> reportExcelVos = refReportExcelVos.stream().sorted().collect(Collectors.toList());
            bsDataReportVo.setExcelTypeList(reportExcelVos);
            List<Long> excelTypeIdList = StreamUtil.of(reportExcelVos).map(v -> {
                return v.getExcelTypeId();
            }).distinct().collect(Collectors.toList());
            bsDataReportVo.setExcelTypeIdList(excelTypeIdList);
            // 样品信息
            if (StrUtil.isNotEmpty(bsDataReportVo.getYpCode())) {
                BsYpVo ypVo = ypMapper.selectVoOne(new LambdaQueryWrapper<BsYp>()
                    .eq(BsYp::getYpNum, bsDataReportVo.getYpCode()));
                if (ObjUtil.isNull(ypVo)) {
                    throw new ServiceException("根据样品编号未能查到样品信息");
                }
                bsDataReportVo.setYpVo(ypVo);
            }
            // 进场台账信息
            if (ObjUtil.isNotNull(bsDataReportVo.getTzId())) {
                BsJctzVo jctzVo = jctzService.queryById(bsDataReportVo.getTzId());
                bsDataReportVo.setTzVo(jctzVo);
            }
        }
        return bsDataReportVo;
    }

    /**
     * 查询数据报告列表
     */
    @Override
    public TableDataInfo<BsDataReportVo> queryPageList(BsDataReportBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<BsDataReport> lqw = buildQueryWrapper(bo);
        Page<BsDataReportVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        result.getRecords().forEach(v -> {
            v.setDeptName(deptMapper.selectVoById(v.getDeptId()).getDeptName());
            v.setSyTypeStr(syTypeMapper.selectById(v.getSyType()).getSyTypeName());
            List<RefReportExcelVo> refReportExcelVos = refReportExcelMapper.selectVoList(new LambdaQueryWrapper<RefReportExcel>()
                .eq(RefReportExcel::getReportId, v.getReportId()));
//                .orderByAsc(RefReportExcel::getExcelIndex));
//            refReportExcelVos.forEach(w -> {
//                w.setLabel(biExcelTypeMapper.selectById(w.getExcelTypeId()).getLabel());
//            });
            // mod 2024-8-26 试验检测日期增加第二个字段 begin
            if (ObjUtil.isNull(v.getReportDateEnd())) {
                v.setShiyrqLabel(ObjUtil.isNotNull(v.getReportDate()) ? DateUtil.format(v.getReportDate(), Constants.EXCEL_DATE_FORMAL) : null);
            } else {
                v.setShiyrqLabel(DateUtil.format(v.getReportDate(), Constants.EXCEL_DATE_FORMAL) + "～"
                    + DateUtil.format(v.getReportDateEnd(), Constants.EXCEL_DATE_FORMAL));
            }
            // mod 2024-8-26 试验检测日期增加第二个字段 end

            // 前端可根据ExcelTypeId 来决定展开哪些报表的填报
            v.setExcelTypeList(refReportExcelVos);
            refReportExcelVos.forEach(x -> {
                x.setExcelTypeName(biExcelTypeMapper.selectVoById(x.getExcelTypeId()).getExcelTypeName());
            });
            v.setJiancxm(CollUtil.join(refReportExcelVos.stream().map(o -> {
                return o.getExcelTypeName();
            }).distinct().collect(Collectors.toList()), ";"));
            // 样品信息
            if (StrUtil.isNotEmpty(v.getYpCode())) {
                BsYpVo ypVo = ypMapper.selectVoOne(new LambdaQueryWrapper<BsYp>()
                    .eq(BsYp::getYpNum, v.getYpCode())
                    .eq(BsYp::getDeptId, bo.getDeptId()));
                if (ObjUtil.isNull(ypVo)) {
                    throw new ServiceException("根据样品编号未能查到样品信息");
                }
                v.setYpVo(ypVo);
            }
        });
        return TableDataInfo.build(result);
    }

    /**
     * 查询数据报告列表
     */
    @Override
    public List<BsDataReportVo> queryList(BsDataReportBo bo) {
        LambdaQueryWrapper<BsDataReport> lqw = buildQueryWrapper(bo);
        List<BsDataReportVo> bsDataReportVos = baseMapper.selectVoList(lqw);
        bsDataReportVos.forEach(v -> {
            v.setDeptName(deptMapper.selectVoById(v.getDeptId()).getDeptName());
            v.setSyTypeStr(syTypeMapper.selectById(v.getSyType()).getSyTypeName());
            List<RefReportExcelVo> refReportExcelVos = refReportExcelMapper.selectVoList(new LambdaQueryWrapper<RefReportExcel>()
                .eq(RefReportExcel::getReportId, v.getReportId()));
//                .orderByAsc(RefReportExcel::getExcelIndex));
//            refReportExcelVos.forEach(w -> {
//                w.setLabel(biExcelTypeMapper.selectById(w.getExcelTypeId()).getLabel());
//            });
            // mod 2024-8-26 试验检测日期增加第二个字段 begin
            if (ObjUtil.isNull(v.getReportDateEnd())) {
                v.setShiyrqLabel(ObjUtil.isNotNull(v.getReportDate()) ? DateUtil.format(v.getReportDate(), Constants.EXCEL_DATE_FORMAL) : null);
            } else {
                v.setShiyrqLabel(DateUtil.format(v.getReportDate(), Constants.EXCEL_DATE_FORMAL) + "～"
                    + DateUtil.format(v.getReportDateEnd(), Constants.EXCEL_DATE_FORMAL));
            }
            // mod 2024-8-26 试验检测日期增加第二个字段 end

            // 前端可根据ExcelTypeId 来决定展开哪些报表的填报
            v.setExcelTypeList(refReportExcelVos);
            refReportExcelVos.forEach(x -> {
                x.setExcelTypeName(biExcelTypeMapper.selectVoById(x.getExcelTypeId()).getExcelTypeName());
            });
            v.setJiancxm(CollUtil.join(refReportExcelVos.stream().map(o -> {
                return o.getExcelTypeName();
            }).distinct().collect(Collectors.toList()), ";"));
            // 样品信息
            if (StrUtil.isNotEmpty(v.getYpCode())) {
                BsYpVo ypVo = ypMapper.selectVoOne(new LambdaQueryWrapper<BsYp>()
                    .eq(BsYp::getYpNum, v.getYpCode()));
                if (ObjUtil.isNull(ypVo)) {
                    throw new ServiceException("根据样品编号未能查到样品信息");
                }
                v.setYpVo(ypVo);
                BeanUtil.copyProperties(ypVo, v, "deptName");
            }
        });
        return bsDataReportVos;
    }

    private LambdaQueryWrapper<BsDataReport> buildQueryWrapper(BsDataReportBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<BsDataReport> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getSyType() != null, BsDataReport::getSyType, bo.getSyType());
        lqw.eq(bo.getDeptId() != null, BsDataReport::getDeptId, bo.getDeptId());
        lqw.like(StringUtils.isNotBlank(bo.getReportName()), BsDataReport::getReportName, bo.getReportName());
        lqw.eq(bo.getReportDate() != null, BsDataReport::getReportDate, bo.getReportDate());
        lqw.between(params.get("beginReportDate") != null && params.get("endReportDate") != null,
            BsDataReport::getReportDate, params.get("beginReportDate"), params.get("endReportDate"));
        lqw.eq(StringUtils.isNotBlank(bo.getBgCode()), BsDataReport::getBgCode, bo.getBgCode());
        lqw.eq(StringUtils.isNotBlank(bo.getYpCode()), BsDataReport::getYpCode, bo.getYpCode());
        lqw.eq(StringUtils.isNotBlank(bo.getJlCode()), BsDataReport::getJlCode, bo.getJlCode());
        lqw.eq(bo.getContractId() != null, BsDataReport::getContractId, bo.getContractId());
        lqw.eq(bo.getExcelNums() != null, BsDataReport::getExcelNums, bo.getExcelNums());
        lqw.eq(bo.getReportResult() != null, BsDataReport::getReportResult, bo.getReportResult());
        lqw.orderByDesc(BsDataReport::getReportDate, BsDataReport::getCreateTime);
        return lqw;
    }

    /**
     * 新增数据报告
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(BsDataReportBo bo) {
        BsDataReport add = BeanUtil.toBean(bo, BsDataReport.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setReportId(add.getReportId());
            if (CollUtil.isNotEmpty(bo.getExcelTypeIdList())) {
                IntStream.range(0, bo.getExcelTypeIdList().size()).forEach(i -> {
                    BiExcelType biExcelType = biExcelTypeMapper.selectById(bo.getExcelTypeIdList().get(i));
                    // 2024年10月14日 mod begin 需求：某些表默认页数不是1。
                    addExcelForSyrwByTypeBo(bo.getReportId(), BeanUtil.toBean(biExcelType, BiExcelTypeBo.class));
                    // 2024年10月14日 mod end
                });
            }
            // 新增或修改 样品信息
            insertOrUpdateYpInfo(bo);
            // TODO 3. 试验室编号规则表修正。
            dealBsRule();
        }
        return flag;
    }

    @Transactional(rollbackFor = Exception.class)
    public void insertOrUpdateYpInfo(BsDataReportBo bo) {
        BsYpVo ypVo = bo.getYpVo();
        BsYp ypEntity = BeanUtil.toBean(ypVo, BsYp.class);
        ypEntity.setYpNum(bo.getYpCode());
        ypEntity.setDeptId(bo.getDeptId());
        ypEntity.setSyType(bo.getSyType());
        // add TaiZhang field
        if (ObjUtil.isNotNull(bo.getTzId())) {
            BsJctzVo bsJctzVo = jctzService.queryById(bo.getTzId());
            Assert.notNull(bsJctzVo, "根据进场台账id未查到进场台账");
            // 代表数量这个字段优先以 试验任务层填的为准，否则取进场台账的代表数量
            if (StrUtil.isEmpty(ypEntity.getDaibsl())) {
                ypEntity.setDaibsl(bsJctzVo.getDaibsl());
            }
            ypEntity.setPih(bsJctzVo.getPih());
            ypEntity.setGuigxh(bsJctzVo.getGuigxh());
            ypEntity.setChandcj(bsJctzVo.getChandcj());
            ypEntity.setInDate(bsJctzVo.getInDate());
        }
        ypMapper.insertOrUpdate(ypEntity);
    }

    private void dealBsRule() {

    }

    @Transactional
    public void addExcelForSyrwByTypeBo(Long reportId, BiExcelTypeBo typeBo) {
        if (ObjUtil.isNull(typeBo.getDefaultNum())) {
            throw new ServiceException("缺少必要参数， excelType：defaultNum");
        }
        for (int num = 0; num < typeBo.getDefaultNum(); num++) {
            handleInsertRefAndXxxBsInfo(reportId, typeBo, num);
        }
    }

    /**
     * @param reportId 试验任务 id
     * @param typeBo   typeBo
     * @param i        typeBo的下标  @@弃用@@ 2024年6月6日 16:55:08 恢复使用2024年10月14日， 同type下的排序
     * @return excelId
     * <p>
     * 为xx试验任务 增加一张 excel
     */
    @Transactional
    public long handleInsertRefAndXxxBsInfo(Long reportId, BiExcelTypeBo typeBo, int i) {
        //  1. 批量新增xxx_bs_info_表
        ReportMapperEnum reportMapperEnum = ReportMapperEnum.findByLabel(typeBo.getLabel());
        if (ObjUtil.isNull(reportMapperEnum)) {
            throw new ServiceException("选中的Excel，简称：" + typeBo.getLabel() + "未注册，请联系管理员");
        }
        Class boClazz = reportMapperEnum.getBoClazz();
        long snowflakeNextId = IdUtil.getSnowflakeNextId();
        try {
            Object o = boClazz.newInstance();
            Field field = boClazz.getDeclaredField("excelId");
            field.setAccessible(true);
            field.set(o, snowflakeNextId);
            Object serviceImpl = SpringUtil.getBean(reportMapperEnum.getServiceClazz());
            try {
                Method insertByBo = reportMapperEnum.getServiceClazz().getDeclaredMethod("insertByBo", boClazz);
                insertByBo.invoke(serviceImpl, o);
            } catch (NoSuchMethodException e) {
                throw new ServiceException("在" + reportMapperEnum.getLabel() + "对应的Service中未找到insertByBo方法");
            } catch (InvocationTargetException e) {
                throw new ServiceException("在" + reportMapperEnum.getLabel() + "对应的Service调用insertByBo方法失败");
            }
            // 2. 批量新增ref_report_excel表
            RefReportExcel ref = new RefReportExcel();
            ref.setExcelId(snowflakeNextId);
            ref.setExcelTypeId(typeBo.getExcelTypeId());
            ref.setReportId(reportId);
            ref.setExcelIndex(i + 1);
            refReportExcelMapper.insert(ref);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            throw new ServiceException("后台处理异常,异常代码handleInsertRefAndXxxBsInfo-error,请联系管理员处理");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ServiceException("后台处理异常,异常代码handleInsertRefAndXxxBsInfo-error,请联系管理员处理");
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ServiceException("后台处理异常,异常代码handleInsertRefAndXxxBsInfo-error,请联系管理员处理");

        }

        return snowflakeNextId;
    }


    /**
     * @param reportId    试验任务 id
     * @param typeBo      typeBo
     * @param oldReportId 原来的试验任务id
     * @param oldExcelId  原 excelId
     * @return excelId
     */
    @Transactional
    public long handleCopyRefAndXxxBsInfo(Long reportId, BiExcelTypeBo typeBo, Long oldReportId, Long oldExcelId) {
        //  1. 批量新增xxx_bs_info_表
        ReportMapperEnum reportMapperEnum = ReportMapperEnum.findByLabel(typeBo.getLabel());
        if (ObjUtil.isNull(reportMapperEnum)) {
            throw new ServiceException("选中的Excel，简称：" + typeBo.getLabel() + "未注册，请联系管理员");
        }
        Class boClazz = reportMapperEnum.getBoClazz();
        long snowflakeNextId = IdUtil.getSnowflakeNextId();
        try {
            Object o = boClazz.newInstance();
            Field field = boClazz.getDeclaredField("excelId");
            field.setAccessible(true);
            field.set(o, snowflakeNextId);
            Object serviceImpl = SpringUtil.getBean(reportMapperEnum.getServiceClazz());

            // 1.5. 批量新增ref_report_excel表
            RefReportExcel ref = new RefReportExcel();
            ref.setExcelId(snowflakeNextId);
            ref.setExcelTypeId(typeBo.getExcelTypeId());
            ref.setReportId(reportId);
            refReportExcelMapper.insert(ref);

            // 2. 插入空数据
            Method insertByBo = reportMapperEnum.getServiceClazz().getDeclaredMethod("insertByBo", boClazz);
            insertByBo.invoke(serviceImpl, o);
            // 3. 查询老的数据, 在fill给新的Bo
            // 3.1 查询自己插入的新数据
            Method queryById = reportMapperEnum.getServiceClazz().getDeclaredMethod("queryById", Long.class);
            Object newBo = queryById.invoke(serviceImpl, snowflakeNextId);
            Object oldBo = queryById.invoke(serviceImpl, oldExcelId);
            BeanUtil.copyProperties(oldBo, newBo,
                CopyOptions.create().setOverride(false));
            // 4. updateByBo
            Method updateByBo = reportMapperEnum.getServiceClazz().getDeclaredMethod("updateByBo", reportMapperEnum.getBoClazz());
            updateByBo.invoke(serviceImpl, BeanUtil.toBean(newBo, boClazz));
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new ServiceException("在" + reportMapperEnum.getLabel() + "对应的Service中未找到方法, 错误详情:" + e.getMessage());
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new ServiceException("在" + reportMapperEnum.getLabel() + "对应的Service调用方法失败, 错误详情:" + e.getTargetException().getMessage());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            throw new ServiceException("没有找到excelId字段" + e.getMessage());
        } finally {
        }
        return snowflakeNextId;
    }

    /**
     * 修改数据报告
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(BsDataReportBo bo) {
        BsDataReport update = BeanUtil.toBean(bo, BsDataReport.class);
        validEntityBeforeSave(update);
        boolean flag = baseMapper.updateById(update) > 0;
        if (flag) {
            if (ObjUtil.isNotNull(bo.getExcelTypeIdList())) {
                // 先删
                refReportExcelMapper.delete(new LambdaQueryWrapper<RefReportExcel>()
                    .eq(RefReportExcel::getReportId, bo.getReportId())
                    .notIn(CollUtil.isNotEmpty(bo.getExcelTypeIdList()), RefReportExcel::getExcelTypeId, bo.getExcelTypeIdList()));
                // 再 增或者不改动
                bo.getExcelTypeIdList().forEach(v -> {
                    if (!refReportExcelMapper.exists(new LambdaQueryWrapper<RefReportExcel>()
                        .eq(RefReportExcel::getReportId, bo.getReportId())
                        .eq(RefReportExcel::getExcelTypeId, v))) {
                        BiExcelType biExcelType = biExcelTypeMapper.selectById(v);
                        // 2024年10月14日 mod begin 需求：某些表默认页数不是1。
                        addExcelForSyrwByTypeBo(bo.getReportId(), BeanUtil.toBean(biExcelType, BiExcelTypeBo.class));
                        // 2024年10月14日 mod end
                    }
                });
            }

            // 新增或修改 样品信息
            insertOrUpdateYpInfo(bo);
            // TODO 3. 试验室编号规则表修正。
            dealBsRule();
        }
        return flag;
    }

    /**
     * 复制一份数据报告
     */
    @Override
    public Boolean copy(BsDataReportBo bo) {
        Long oldReportId = bo.getReportId();
        BsDataReport copy = BeanUtil.toBean(bo, BsDataReport.class);
        long newReportId = IdUtil.getSnowflakeNextId();
        copy.setReportId(newReportId);
        validEntityBeforeSave(copy);
        // 1 新增 report 表
        boolean flag = baseMapper.insert(copy) > 0;
        if (flag) {
            bo.setReportId(copy.getReportId());
            // 2. 新增 样品信息 (注意 清空样品id 自适应新增)
            bo.getYpVo().setYpId(null);
            insertOrUpdateYpInfo(bo);
            // 3. 处理 excel
            if (CollUtil.isNotEmpty(bo.getExcelTypeList())) {
                IntStream.range(0, bo.getExcelTypeList().size()).forEach(i -> {
                    BiExcelType biExcelType = biExcelTypeMapper.selectById(bo.getExcelTypeList().get(i).getExcelTypeId());
                    // 3.1 新增空数据 到 xxx_bs_info\ xxx_ba_detail\ ref 表 & 填充数据 到 xxx_bs_info\ xxx_ba_detail
                    long newExcelId = handleCopyRefAndXxxBsInfo(newReportId, BeanUtil.toBean(biExcelType,
                        BiExcelTypeBo.class), oldReportId, bo.getExcelTypeList().get(i).getExcelId());

                });
            }

            // TODO 3. 试验室编号规则表修正。
            dealBsRule();
        }
        return flag;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(BsDataReport entity) {
        // 做一些数据校验,如唯一约束
        // 只能本试验室人员
        deptService.isLocalDept(entity.getDeptId());
        if (baseMapper.exists(new LambdaQueryWrapper<BsDataReport>()
            .eq(BsDataReport::getBgCode, entity.getBgCode())
            .ne(ObjUtil.isNotEmpty(entity.getReportId()), BsDataReport::getReportId, entity.getReportId()))) {
            throw new ServiceException("该报告编号已被使用，请更换报告编号后重试");
        }
        if (baseMapper.exists(new LambdaQueryWrapper<BsDataReport>()
            .eq(BsDataReport::getYpCode, entity.getYpCode())
            .ne(ObjUtil.isNotEmpty(entity.getReportId()), BsDataReport::getReportId, entity.getReportId()))) {
            throw new ServiceException("该样品编号已被使用，请更换样品编号后重试");
        }
        if (baseMapper.exists(new LambdaQueryWrapper<BsDataReport>()
            .eq(BsDataReport::getJlCode, entity.getJlCode())
            .ne(ObjUtil.isNotEmpty(entity.getReportId()), BsDataReport::getReportId, entity.getReportId()))) {
            throw new ServiceException("该记录编号已被使用，请更换记录编号后重试");
        }
    }

    /**
     * 批量删除数据报告
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        if (refReportExcelMapper.exists(new LambdaQueryWrapper<RefReportExcel>().in(RefReportExcel::getReportId, ids))) {
            throw new ServiceException("任务内包含试验记录或者报告，需删除内部记录或者报告之后才能删除");
        }
        // 删除对应的样品
        List<BsDataReportVo> bsDataReportVos = baseMapper.selectVoBatchIds(ids);
        List<String> ypNumList = bsDataReportVos.stream().map(BsDataReportVo::getYpCode).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(ypNumList)) {
            ypMapper.delete(new LambdaQueryWrapper<BsYp>().in(BsYp::getYpNum, ypNumList));
        }
        // 删除试验任务
        boolean flag = baseMapper.deleteBatchIds(ids) > 0;
        return flag;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean tb(List<ReportBo> bos) throws ServiceException {
        if (CollUtil.isNotEmpty(bos)) {
            for (ReportBo bo : bos) {
                ReportMapperEnum anEnum = ReportMapperEnum.findByLabel(bo.getLabel());
                if (ObjUtil.isNull(anEnum)) {
                    throw new ServiceException("选中的Excel，简称：" + bo.getLabel() + "未注册，请联系管理员");
                }
                List<BiExcelTypeVo> biExcelTypeVos = biExcelTypeMapper.selectVoList(new LambdaQueryWrapper<BiExcelType>()
                    .eq(BiExcelType::getLabel, bo.getLabel()));
                if (CollUtil.isEmpty(biExcelTypeVos)) {
                    throw new ServiceException("根据label未找到对应的Excel");
                }
                String excelTypeName = biExcelTypeVos.get(0).getExcelTypeName();
                Object serviceImpl = SpringUtil.getBean(anEnum.getServiceClazz());
                try {
                    Method updateByBo = anEnum.getServiceClazz().getDeclaredMethod("updateByBo", anEnum.getBoClazz());
                    updateByBo.invoke(serviceImpl, BeanUtil.toBean(bo.getExcelObj(), anEnum.getBoClazz()));
                } catch (ServiceException e) {
                    log.error(e.getMessage());
                    throw e;
                } catch (NoSuchMethodException e) {
                    log.error(e.getMessage());
                    e.printStackTrace();
                    throw new ServiceException(excelTypeName + "保存失败; 具体原因:在简称为" + anEnum.getLabel() + "注册的Service中未找到updateByBo方法,请联系管理员");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    throw new ServiceException(excelTypeName + "保存失败; 具体原因:后台处理异常,tb-error,请联系管理员处理" + e.getMessage());
                } catch (InvocationTargetException e) {
                    log.error(e.getMessage());
                    e.printStackTrace();
                    if (ServiceException.class.isInstance(e.getTargetException())) {
                        throw new ServiceException(e.getTargetException().getMessage());
                    }
                    throw new ServiceException(excelTypeName + "保存失败; 具体原因:后台处理异常,tb-error,请联系管理员处理" + e.getMessage());
                } catch (Exception e) {
                    log.error(e.getMessage());
                    e.printStackTrace();
                    throw new ServiceException(excelTypeName + "保存失败; 具体原因:后台处理异常,tb-error,请联系管理员处理" + e.getMessage());
                }
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modPages(ModPagesBo bo) {
        BiExcelType biExcelType = biExcelTypeMapper.selectOne(new LambdaQueryWrapper<BiExcelType>()
            .eq(BiExcelType::getLabel, bo.getLabel()));
        if ("1".equals(bo.getModType())) {
            // 加页
            handleInsertRefAndXxxBsInfo(bo.getReportId(), BeanUtil.toBean(biExcelType, BiExcelTypeBo.class), 999);
        }
        if ("-1".equals(bo.getModType())) {
            // 减页
            if (ObjUtil.isNull(bo.getExcelId())) {
                throw new ServiceException("减页时excelId不能为空");
            }
            if (refReportExcelMapper.selectCount(new LambdaQueryWrapper<RefReportExcel>()
                .eq(RefReportExcel::getReportId, bo.getReportId())
                .eq(RefReportExcel::getExcelTypeId, biExcelType.getExcelTypeId())) == 1L) {
                throw new ServiceException("该类型表单仅剩1页,无法减页");
            }
            refReportExcelMapper.delete(new LambdaQueryWrapper<RefReportExcel>()
                .eq(RefReportExcel::getReportId, bo.getReportId())
                .eq(RefReportExcel::getExcelId, bo.getExcelId()));
        }
        return Boolean.TRUE;
    }

    @Override
    public QrCodeVo qrcode(Long reportId) {
        List<RefReportExcelVo> refReportExcelVos = refReportExcelMapper.selectVoList(new LambdaQueryWrapper<RefReportExcel>()
            .eq(RefReportExcel::getReportId, reportId));
        refReportExcelVos.forEach(v -> {
            v.setVo(biExcelTypeMapper.selectById(v.getExcelTypeId()));
        });
        List<RefReportExcelVo> bgExcelList = refReportExcelVos.stream().filter(v -> {
            ReportMapperEnum reportMapperEnum = ReportMapperEnum.findByLabel(v.getLabel());
            return reportMapperEnum.getBgFlag();
        }).collect(Collectors.toList());
        if (CollUtil.isEmpty(bgExcelList)) {
            throw new ServiceException("该试验任务内没有试验报告，无法生成二维码");
        }
        QrCodeVo result = new QrCodeVo();
        result.setUrl(url + reportId);
        return result;
    }

    @Override
    public List<ExcelGeneralVo> bgInfos(Long reportId) {
        BsDataReportVo reportVo = baseMapper.selectVoById(reportId);
        List<RefReportExcelVo> refReportExcelVos = refReportExcelMapper.selectVoList(new LambdaQueryWrapper<RefReportExcel>()
            .eq(RefReportExcel::getReportId, reportId));
        refReportExcelVos.forEach(v -> {
            v.setVo(biExcelTypeMapper.selectById(v.getExcelTypeId()));
        });
        // 筛选出报告表， 过滤掉记录表
        List<RefReportExcelVo> bgExcelList = refReportExcelVos.stream().filter(v -> {
            ReportMapperEnum reportMapperEnum = ReportMapperEnum.findByLabel(v.getLabel());
            return reportMapperEnum.getBgFlag();
        }).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(bgExcelList)) {
            ArrayList<ExcelGeneralVo> vos = new ArrayList<>();
            bgExcelList.forEach(v -> {
                ReportMapperEnum reportMapperEnum = ReportMapperEnum.findByLabel(v.getLabel());
                BaseMapperPlus mapper = SpringUtil.getBean(reportMapperEnum.getMapperClazz());
                ExcelGeneralVo result = null;
//                try {
//                    Method selectVoById = reportMapperEnum.getMapperClazz().getDeclaredMethod("selectVoById", Long.class);
//                    result = (ExcelGeneralVo) selectVoById.invoke(mapper, v.getExcelId());
                result = (ExcelGeneralVo) mapper.selectVoById(v.getExcelId());
                if (ObjUtil.isNull(reportVo.getReportDateEnd())) {
                    result.setShiyjcrq(ObjUtil.isNotNull(reportVo.getReportDate()) ? DateUtil.format(reportVo.getReportDate(), Constants.EXCEL_DATE_FORMAL) : null);
                } else {
                    result.setShiyjcrq(DateUtil.format(reportVo.getReportDate(), Constants.EXCEL_DATE_FORMAL) + "～"
                        + DateUtil.format(reportVo.getReportDateEnd(), Constants.EXCEL_DATE_FORMAL));
                }
                result.setBaogmc(v.getExcelTypeName());
                result.setQianszt("待编制");
//                }
//                catch (NoSuchMethodException e) {
//                    e.printStackTrace();
//                    throw new ServiceException("在" + reportMapperEnum.getLabel() + "对应的Service中未找到queryById方法");
//                } catch (IllegalAccessException e) {
//                    e.printStackTrace();
//                    throw new ServiceException("后台处理异常,异常代码bgInfos-error,请联系管理员处理");
//                } catch (ServiceException e) {
//                    throw e;
//                } catch (InvocationTargetException e) {
//                    // 空指针等错误
//                    e.printStackTrace();
//                    throw new ServiceException("在" + reportMapperEnum.getLabel() + "对应的Service调用queryById方法失败, 参数：excelId=" + v.getExcelId());
//                }

                vos.add(result);
            });
            return vos;
        }
        return ListUtil.empty();
    }


    /**
     * 判断该试验任务下，所有的excel是否都合格
     */
    @Override
    public boolean isAllOk(List<RefReportExcelVo> refReportExcelVos) {
        if (CollUtil.isNotEmpty(refReportExcelVos)) {
            for (RefReportExcelVo refExcelVo : refReportExcelVos) {
                // 1. 判断是不是 报告Excel
                BiExcelTypeVo typeVo = biExcelTypeMapper.selectVoById(refExcelVo.getExcelTypeId());
                ReportMapperEnum anEnum = ReportMapperEnum.findByLabel(typeVo.getLabel());
                if (!anEnum.getBgFlag()) {
                    continue;
                }
                // 2. 判断是不是不合格，如果不合格则返回 false
                Object serviceImpl = SpringUtil.getBean(anEnum.getServiceClazz());
                Boolean excelResult = null;
                try {
                    Method isWell = anEnum.getServiceClazz().getDeclaredMethod("isWell", Long.class);
                    excelResult = (Boolean) isWell.invoke(serviceImpl, refExcelVo.getExcelId());
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                if (!excelResult) {
                    return false;
                }
            }
            return true;
        }
        return true;
    }

}
